\chapter[Descrizione della libreria]%
{Fast Hessian Detector}

\section{Il descrittore}
Il rivelatore SURF si basa sul determinante della matrice hessiana. Al fine di incentivare l'uso della tela di iuta, consideriamo una funzione continua di due variabili tale che il valore della funzione a (x, y) è data da f (x, y).

La matrice hessiana, H, è la matrice delle derivate parziali della funzione f:

􏰆 ∂ 2f ∂ 2f 􏰇
∂ x2 ∂ x ∂ y H (f (x, y)) = ∂ 2f ∂ 2f (3)
X ∂ y ∂ ∂ y2


Il determinante di questa matrice, nota come discriminante, è calcolata:


∂ 2f ∂ 2f 􏰄 ∂ 2f 􏰅 2 det (H) = ∂ x2 ∂ y2 - ∂ x ∂ y (4)


Il valore della discriminante è utilizzato per classificare i massimi ed i minimi della funzione con il test del secondo ordine derivati​​.

Poiché il determinante è il prodotto di autovalori della matrice, possiamo classificare i punti in base al segno del risultato. 

Se il determinante è negativo, gli autovalori hanno segni diversi e, quindi, il punto non è un estremo locale; se è positivo allora o entrambi gli autovalori sono positivi o entrambi sono negativi e in ogni caso il punto è classificato come estremo.

Tradurre questa teoria per lavorare con immagini piuttosto che una funzione continua {\' e} un compito abbastanza banale.
Per prima cosa, bisogna sostituire i valori della funzione f (x, y) con l'immagine pixel {\em Intensi-ties} I (x, y).
Poi abbiamo bisogno di un metodo per calcolare le derivate parziali del secondo ordine.

Siamo in grado di calcolare le derivate di convoluzione con un kernel appropriato. 

Nel caso di features della scala di secondo ordine la gaussiana normalizzata {\' e} il filtro scelto, in quanto consente l'analisi su scale o su spazi ({\em teoria di scale-space})

Siamo in grado di costruire kernel per i derivati della ​​gaussiana in x, y e combinati in modo tale che tramite la direzione xy riusciamo a calcolare le quattro voci della matrice hessiana.

L'uso della gaussiana ci permette di variare l'ammontare di sfocatura durante la fase di convoluzione in modo che il determinante sia calcolato a scale diverse.

Inoltre, poich{\' e} la gaussiana {\' e} una isotropo (cio{\' e} circolarmente simmetrica) alla funzione, grazie al kernel di convoluzione possiamo operare sfruttando la propriet{\' a} di invarianza rotativa

Ora possiamo calcolare la matrice Hessiana H: come funzione sia spazio x = (x, y) e σ scala.

􏰆 Lxx (x, σ) Lxy (x, σ)
Lxy (x, σ) 􏰇 Lyy (x, σ) H (x, σ) =

Qui L (x, σ) si riferisce alla convoluzione del secondo ordine gaussiano della derivata di ∂ 2G (σ)xx ∂ x2 con l'immagine nel punto x = (x, y) e analogamente per Lyy e Lxy.

Questi derivati ​​sono noti come {\em Laplaciani di gaussiane}.
Partendo da qui si pu{\' o} lavorare singolarmente su ogni pixel dell'immagine e utilizzare il valore per determinarne i punti di interesse

Questa variazione del rivelatore Hessiano è simile a quella proposta da Beaudet [3].

R. Lowe ha riscontrato un aumento delle prestazioni approssimando la laplaciana di gaussiane da una differenza di gaussiane. 

In modo simile, S. Bay ha proposto una approssimazione alla laplaciana di gaussiane utilizzando rappresentazioni-filtro del kernel delle immagini. 

La {\em figura 2} illustra la somiglianza tra i kernel discretizzati e ritagliati e le loro controparti filtrate. 
Notevole incremento di prestazioni si {\' e} riscontrato quando questi filtri vengono utilizzati in combinazione con l'immagine integrale descritta nella sezione introduttiva. 

Per quantificare la differenza si può considerare il numero di accessi alla matrice e manipolazioni necessarie alla convoluzione. 

Per una 9 × 9 filtro avremmo bisogno di 81 accessi array e le operazioni per l'originale rappresentazione del filtro a valori reali e solo 8 peril filtro. 
Poiché il filtro è aumentato di formato, il costo di calcolo aumenta in modo significativo per la laplaciana originale mentre il costo per i filtri è indipendente dalle dimensioni.

In {\em Figura 2} i pesi applicati a ciascuna delle sezioni di filtro sono semplificati come segue: per il {\em filtro Dxy} le regioni nere sono ponderate con un valore di 1, le regioni bianche con un valore pari a -1 e le altre aree ponderate a variabili aleatoree. I filtri {\em Dxx} e {\em Dyy} sono ponderati in modo simile ma le regioni bianche hanno un valore di -1 e le nere un valore di 2. 
Una valutazione semplice permette un rapido calcolo delle aree, ma nell'utilizzo di questi valori dobbiamo affrontare la differenza di valori di risposta tra il kernel originale e approssimato.

Bay propone la seguente formula come approssimazione per il calcolo del determinante dell'essiana utilizzando una gaussiana approssimata:


det (Happrox) = DxxDyy - (0.9Dxy) 2 (1)


In [1], i due filtri vengono confrontati in dettaglio i risultati e si evince che la perdita (trascurabile) della rappresentazione filtrata è di gran lunga superata dal notevole aumento di efficienza e velocit{\' a}. 

Il determinante qui viene indicato come la risposta {\em blob} alla posizione x = (x, y, σ). 

La ricerca dei massimi locali di questa funzione avviene su scala spaziale, e produce i punti di interesse per un'immagine. 

\subsection{Test per la derivata seconda}
Il seguente criterio può essere applicato in un punto critico non degenere x:
se l'hessiana è una matrice definita positiva in x, allora f ha un minimo locale in x;
se l'hessiana è una matrice definita negativa in x, allora f ha un massimo locale in x;
se l'hessiana ha tutti gli autovalori non nulli e di entrambi i segni allora x è un punto di sella per f.
Altrimenti il test è inconclusivo. Nota che per hessiane semidefinite positive e semidefinite negative il test è inconclusivo. Quindi, possiamo vedere di più dal punto di vista della teoria di Morse.
Tenuto conto di quanto è stato appena detto, il test per le derivate seconde per funzioni di una e due variabili sono semplici.
In una variabile, l'hessiana contiene appena una derivata seconda:
se questa è positiva allora x è un minimo locale, se questa è negativa allora x è un massimo locale;
se questa è zero allora il test è inconclusivo.
In due variabili, può essere usato l'hessiano, perché è il prodotto degli autovalori:
se questo è positivo allora gli autovalori sono entrambi positivi, o entrambi negativi;
se questo è negativo allora i due autovalori hanno differente segno;
se questo è zero, allora il test della derivata seconda è inconclusivo.

\section{Costruzione dello spazio in scala}
Al fine di individuare i punti di interesse utilizzando il determinante della Hessiana, {\' e} prima necessario introdurre la nozione di uno {\em spazio in scala}. 

Uno spazio in scala {\' e} una funzione continua che può essere utilizzato per trovare estremi attraverso tutte le scale possibili. 
Nella visione artificiale, lo spazio in scala {\' e} tipicamente implementato come una piramide di immagini in cui l'immagine di ingresso {\' e} iterativamente convoluta con kernel gaussiano e ripetutamente sub-campionato (riducendone le dimensioni). 

Questo metodo viene utilizzato con grande effetto in SIFT [4], ma dal momento che ogni strato si basa sul precedente, e le immagini devono essere ridimensionate, non risulta essere computazionalmente efficiente. 

Poich{\' e} il tempo di elaborazione del kernel utilizzati in SURF {\' e} invariante alle dimensioni, lo spazio in scala può essere creato mediante l'applicazione di kernel per aumentare le dimensioni dell'immagine originale. 

Questo permette a pi{\' u} livelli della piramide di essere trattati simultaneamente ed elimina la necessit{\' a} di sotto-campionare l'immagine, consentendo in tal modo un ulteriore incremento delle prestazioni.

Lo spazio in scala {\' e} suddiviso in un multiplo di ottave, dove un'ottava si riferisce ad una serie di mappe che coprono la risposta di un raddoppio di scala. 

In SURF il livello più basso della scala {\' e} ottenuta generando filtri 9x9. 

Questi filtri corrispondono a un vero e proprio valore di gaussiana con σ = 1.2. 
Strati successivi sono ottenuti con la conversione dei filtri, pur mantenendo lo stesso rapporto di layout della finestra. 

Con l'aumentare della dimensione del filtro, aumenta anche il valore della scala derivanti gaussiana, e poiché i rapporti del layout rimangono costanti possiamo calcolare questa scala con la formula:


σapprox = filtro corrente · Dimensioni base del filtro

Scala Base dimensione del filtro = Dimensione filtro corrente · 1,2 9

Quando si costruiscono grandi filtri, ci sono una serie di fattori che devono essere prendere in considerazione. 

L'aumento di dimensione è limitato dalla lunghezza dei fattori positivi e negativi delle derivate gaussiane di secondo ordine. 

Nei filtri approssimati, le dimensioni dei fattori sono fissate a un terzo della lunghezza del lato del filtro e si riferisce alla lunghezza del lato più corto delle regioni ponderate in bianco e nero. 

Dal momento che richiedono la presenza di un pixel centrale, le dimensioni devono essere aumentate in modo uniforme intorno questa posizione e quindi la grandezza della scala può aumentare da un fattore minimo di 2. 
Poiché ci sono tre canali in ogni filtro (che deve essere della stessa dimensione), il pi{\' u}  piccolo passo tra filtri consecutivi {\' e} 6. Per il Dxx e Dyy, la lunghezza del lato più lungo degli aumenti ponderati rimane 2 su ogni lato, per preservarne la struttura.

\subsection{Analisi e localizzazione dei punti di interesse}

Il compito di localizzare la scala e la rotazione dei punti di interesse invarianti a rotazioni e omografie può essere suddiviso in tre fasi. 

In primo luogo le regioni sono thresholded in modo che tutti i valori al di sotto della soglia predeterminata vengano rimossi. 
Aumentando la soglia si abbassa il numero di punti di interesse rilevati, lasciando solo i pi{\' u} consistenti; diminuendola si permette a molti pi{\' u} punti di essere rilevati. 
Pertanto, la soglia pu{\' o} essere adattata a seconda del contesto di applicazione.

Dopo la soglia, una soppressione non massimale viene eseguita per trovare un insieme di punti candidati.
Per fare questo ogni pixel nello spazio viene confrontato con i suoi 26 vicini, composti da 8 punti nella scala nativi e il 9 viene estratto da ciascuna delle scale vicine. 

A questo punto abbiamo una serie di punti di interesse con resistenza minima determinata dal valore di soglia e che sono anche massimi / minimi locali nello spazio.

La fase finale tende a localizzare i punti di interpolazione dei dati a comportamento ravvicinato,per trovare la posizione nello spazio e scala di precisione sub-pixel. 

Ci{\' o} viene effettuato istanziando un 3D-quadratico, come proposto da [3]. 
Per fare questo abbiamo espresso il determinante della funzione dell'essiana H (x, y, σ), come una espansione del polinomio di Taylor fino a termini quadratici centrato nella posizione rilevata. 

Questo viene espresso come:


HT 1 T ∂ ∂ 2H H (x) H = + ∂ x ∂ x 2 x x2x (7)


La posizione interpolata di estremo, x = (x, y, σ), si trova prendendo la derivata di questa funzione e (se impostato a zero):


x = - ∂ 2H -1 ∂ H (8) ∂ x2 ∂ x


Le derivate ​​qui sono approssimate con differenze finite di pixel vicini. 

Se x è maggiore di 0,5 nel X, Y si regola la posizione e si esegue l'interpolazione di nuovo.

Questa procedura viene ripetuta fino a quando x è inferiore a 0,5 in tutte le direzioni o il numero di passi di interpolazione predeterminato è stato superato. 
Quei punti che non convergono vengono eliminati dal set di punti di interesse, lasciando solo il più stabile e ripetibile.

\chapter{I punti di interesse}
Il descrittore SURF descrive come le intensit{\' a} dei pixel sono distribuiti all'interno di una
scala  a carico di ciascun punto di interesse individuate dall'essiana

Questo approccio è simile a quello di SIFT , ma le immagini integrali utilizzate in combinazione
con filtri noti come {\em Haar-wavelet} sono utilizzate al fine di aumentare la robustezza e
diminuire il tempo di computazion. Questi sono semplici filtri che possono essere utilizzati per
trovare gradienti nelle direzioni x e y.

L'estrazione del descrittore pu{\' o} essere suddiviso in due operazioni distinte.

In primo luogo, ad ogni punto di interesse {\' e} assegnato un orientamento riproducibile prima di una scala
Questa finestra indipendente è costruita tramite un vettore di 64 bit.

{\' E}importante che tutti i calcoli per il descrittore siano basati su misurazioni
rispetto alla scala rilevata, al fine di ottenere risultati invariante alle rotazioni.

La procedura per estrarre il descrittore è spiegata più approfonditamente nella sezione seguente

\subsection{Orientamento del descrittore}

Al fine di mantenere l'invarianza per rotazione delle immagini, ad ogni punto di interesse rilevato viene assegnato un orientamento riproducibile. 

L'estrazione dei componenti del descrittore viene eseguita rispetto a questa direzione, quindi {\' e} importante che questa direzione si trova ad essere ripetibile in condizioni variabili. 
Per determinare l'orientamento, le risposte Haar di 4σ dimensioni sono calcolate per una serie pixel entro un raggio di 6σ del punto rilevato, dove σ si riferisce al livello in cui è stato rilevato il punto. 

L'insieme di pixel specifico {\' e} determinato mediante campionamento quelli all'interno del cerchio con una distanza di σ.
Le risposte sono ponderate con una gaussiana centrata nel punto di interesse.

In linea con il resto della gaussiana, dipende dalla scala del punto scelto se ottenere 2.5σ come deviazione standard. 
Una volta pesate, le risposte sono rappresentate come punti nello spazio vettoriale, con x lungo l'ascissa e y lungo l'ordinata.

L'orientamento domimante viene selezionato dalla rotazione di un segmento di cerchio che copre un angolo di
π intorno all'origine. Ad ogni posizione, le coordinate x e y all'interno del segmento sono sommate e utilizzate per formare un nuovo vettore. 

Il più lungo vettore presta il suo orientamento al punto di interesse che stiamo considerando.

In alcune applicazioni, l'invarianza in rotazione non {\' e} richiesta, quindi questo passaggio può essere omesso, fornendo in tal modo un ulteriore aumento delle prestazioni. 
In [1] questa versione del descrittore viene indicato come {\em Upright SURF (o U-SURF)} e ha dimostrato di mantenere la robustezza per le rotazioni di immagini fino a + / - 15 gradi.

\subsection{Componenti del descrittore}

Il primo passo per l'estrazione del descrittore SURF è quello di costruire una finestra quadrata attorno al punto di interesse. 
Questa finestra contiene i pixel che formano le voci nel vettore ed è di dimensioni 20σ, dove σ si riferisce al livello rilevato. Inoltre la finestra è orientato lungo la direzione trovata al punto precedente in modo che tutti i calcoli successivi sono relativi a questa direzione.

La finestra del descrittore è divisa in 4 × 4 sottoregioni regolari. All'interno di ciascuna di queste subregioni  vengono calcolate le Haar di 2σ dimensioni per 25 punti di campionamento distribuiti regolarmente. 
Se ci riferiamo a x e y da dx e dy, rispettivamente, allora per questi 25 punti di campionamento (cioè ogni regionali) che raccogliamo vale:


vsubregion dy = 􏰊 􏰉 dx, 􏰉, 􏰉 | dx |, 􏰉 | dy | 􏰋. (9)


Quindi ogni subregione contribuisce con quattro valori per il vettore di descrittore, che porta ad un vettore globale di lunghezza 4 × 4 × 4 = 64. 
Il descrittore risultante SURF è invariante per rotazione, dimensioni, luminosità e, dopo la riduzione di unità di lunghezza, di contrasto.

\chapter{Design}

Questa sezione descrive le scelte progettuali che sono stati effettuate per realizzare l'algoritmo

\section{Linguaggio e ambiente}

C + + è stato scelto come linguaggio di programmazione per lo sviluppo per i seguenti motivi:

1. Velocità: l'elaborazione a basso livello immagine deve essere veloce ed efficiente

2. Usabilità: Nella mia ricerca, quasi tutti gli algoritmi di elaborazione delle immagini sembra sono implementati in C + +, C e Matlab. 

3. Portabilità: C + + può essere interamente portabile su più piattaforme e compilatori seguendo precisi parametri

4. Image Processing Library: OpenCV è una libreria C + + che ben si presta a complesse elaborazioni di immagini in computer vision. Essa fornisce funzionalità per la lettura dei dati da file di immagini, file video così come il video in diretta da una webcam o altro dispositivo di visione. La libreria è ben supportata e funziona sia su Linux che su Windows.
OpenCV si integra bene anche con il compilatore  gcc (4.5.3) ed è pienamente supportata. 

\section{Design architetturale}
Il disegno di architettura fornisce una scomposizione top-down della libreria in moduli e classi.

{\em Integral Image}
Descrizione: Il modulo crea e gestisce l'integrale delle immagini 

INPUT: Un processo.

IMMAGINE: crea la rappresentazione integrale di un immagine dell'immagine in ingresso. Calcolo tardivo di somme di pixel sulla verticale delle aree rettangolari. 

OUTPUT: La rappresentazione di immagini integrali.

{\em Fast-Hessian}

Descrizione: Trova i punti di interesse sulla base della matrice / regione in una data immagine integrale. 

INPUT: Una rappresentazione di immagini integrate di un'immagine. 

THREADS: Costruisce il determinante della mappatura dell'essiana. Esegue una suppresion non massimale di localizzazione dei punti di interesse in uno spazio. Interpola i punti rilevati per la 
precisione sub-pixel.

OUTPUT: Un vettore di punti di interesse accuratamente localizzati.

{\em SURF Descriptor}

Descrizione: Estrae il descrittore dei componenti per un dato insieme di punti di interesse rilevati. 

INPUT: Una rappresentazione di immagini integrate di un'immagine, vettore dei punti di interesse. 

THREADS: Calcola risposte per il filtro Haar. Calcola l'orientamento dominante di un punto di interesse. Vettore descrittore a 64 bit basato sulle somme dei filtri.

OUTPUT: Un vettore di punti di interesse.

{\em Punto di interesse}

Descrizione: Memorizza i dati associati a ogni punto di interesse individuale. 

INPUT: Vettore dei punti di interesse. 

THREADS: Accessor / Mutator metodi di dati. 

OUTPUT: Nessuno

{\em Utilities}

Descrizione: Il modulo contiene tutte le funzioni ausiliarie.


\chapter{Implementazione}
In base all'analisi teorica delle sezioni precedenti, l'applicazione risultante dell'algoritmo SURF è descritta di seguito.

Questa sezione serve a fornire ulteriori informazioni sulle SURF e allo stesso tempo chiarire i concetti che non sono esplicitamente definiti. Per ogni componente dei metodi che sono stati usati sono dimostrati da un approccio algoritmico, con stralci di codice fornito ove necessario.

\section{Immagini integrali}
Il calcolo dell'integrale è un processo molto semplice. Si itera su tutte le righe e le colonne in modo che il valore dell'immagine integrale ad ogni punto sia la somma di pixel in alto a sinistra. Per implementare questo in maniera efficiente, si calcola la somma cumulativa per riga, mentre ci si muove lungo ogni colonna. 
Per la prima riga questa somma costituisce il valore in ogni punto dell'immagine risultante dell'integrale. Per ogni riga successiva, il valore risultante è la somma cumulativa della riga più il valore nella cella sopra l'immagine integrale.
Lo spezzone di codice inerente e' riportato di seguito:

\section{Matrice hessiana}
Il primo passo nel processo di filtro Fast-Hessian è quello di costruire la mappa blob per il numero desiderato di ottave e intervalli. 
Questo è implementato come un array a 2 dimensioni delle strutture di immagine di intervalli × ottave dimensioni. 

Quindi se si vuole analizzare un spazio composto di tre ottave e quattro intervalli, come è l'impostazione predefinita, si crea un array di dodici immagini. 
I valori per ogni coppia di ottava-intervallo dettano le dimensioni del filtro che verrà convoluto a quel livello nello spazio. La grandezza del filtro è data dalla formula:


Filter size = 3 􏰂 intervallo 2octave + 1 × 􏰃. (10)


La dimensione prevista di filtro per il primo intervallo della prima ottava è la base di 9 × 9 filtro come descritto precedentemente. 

Utilizzando la formula con i valori di ottava interval = = 1 otteniamo:


FilterSize = 3 􏰀 21 × 1 +1 = 9 􏰁.


Usando questa formula siamo in grado di costruire e condensare entrambi i filtri ad ogni livello dello spazio in automatico. 

Al fine di evitare effetti di bordo nella convoluzione, abbiamo stabilito un confine per ogni ottava tale che il maggior filtro nell'ottava resti interamente sopra l'immagine. 

Questo è importante in quanto effetti di bordo possono dare risultati anomali che riducono l'affidabilità del rivelatore. 

Una volta che le risposte sono calcolate per ogni livello dello spazio-scala, è importante che essi siano su scala normalizzata. 

La normalizzazione è un passo importante che garantisce che non vi sia alcuna riduzione nei filtri con l'aumentare della sottostante scala gaussiana. 
Per i filtri approssimati, la normalizzazione si ottiene dividendo la risposta di ogni singola zona. Questo tende ad una perfetta invarianza di scala. 

Il listato di codice seguente mostra uno spoglio approccio alla creazione della mappatura:

\input{proges}

La ricerca delle estremità nella mappatura è condotta da una  soppressione forzata. 

L'attuazione di questo metodo è molto semplice: ogni pixel viene confrontato con i suoi vicini e solo quelli che sono più grandi di tutti coloro che li circondano sono classificati come punti di interesse. 
Il costo del controllo dei valori vicini è relativamente basso, in qauanto il numero di pixel sarà determinato come massimale dopo solo qualche controllo.


La posizione interpolata e la scala dei punti di interesse rilevata è trovata con il filtro essiano precedentemente descritto. Questo può essere calcolato mediante una semplice matrice e poche moltiplicazioni. 

Per trovare la posizione interpolata, x = (x, y, σ), in scala, abbiamo bisogno di calcolare il risultato tramite:


x = - ∂ 2 H - 1 ∂ H. ∂ x2 ∂ x


Per fare questo abbiamo bisogno di calcolare prima le voci nel 3 × 3 2H ∂ matrice e il 3 × 1 ∂ x2 vettore ∂ H. 

Queste voci sono calcolate con differenze finite di intensità dei pixel e la ∂, i cui valori specifici sono dati da:

Qui d riferisce a ∂ I, D si riferisce a ∂ 2I e così pure per le altre voci. 

Grazie a questi due descrittori

x xx x ∂ ∂ x2 

tramite le matrici di una semplice inversione e moltiplicazione di matrici si ottiene la posizione interpolata.


Ogni punto di interesse interpolato viene aggiunto a un vettore che è l'output finale del filtro essiano.

\section{Descrittore}
Come descritto nella sintesi tecnica del surf, il primo passo nel descrivere i punti di interesse trovati dal rilevatore è di estrarre l'orientamento dominante. 
In fase di esecuzione questo è suddiviso in due fasi, dapprima il calcolo dei filtri Haar intorno al punto e in secondo luogo un iterazione della regione per calcolare le risposte che rientrano in un segmento della finestra.

Il calcolo delle risposte è un compito banale. 
Considerando una regione quadrata di 12σ dimensione centrata sul punto di interesse, si calcolano solo quelle risposte la cui distanza è inferiore a 6σ dal centro. 
Queste risposte sono ponderate con la funzione gaussiana e aggiunte ad un vettore. 

Per calcolare le risposte all'interno della finestra, si passa attraverso un insieme discreto di angoli compresi tra 0 e 2π e si calcola la somma delle risposte che formano un angolo all'interno della finestra.

L'strazione del descrittore è implementata in maniera molto simile al calcolo di risposta per l'assegnazione di orientamento. 
Invece di una regione circolare, il descrittore utilizza una finestra rettangolare. 

Nel codice, un ciclo si ripete su ciascuno delle 16 sottoregioni, mentre un altro calcola le 25 wavelet all'interno di ciascuna. 

Dal momento che le finestre sono orientate lungo la direzione dominante, e l'immagine integrale e' recuperabile solo alle estremità dell'area di selezione, si propone un interpolazione delle risposte alle invarianti di rotazione. 

Procedendo cosi, piuttosto che interpolando le risposte, le finestre del descrittore vengono prima ruotate in modo che l'orientamento sia dominante in linea con il positivo asse Y. 

C'è un piccolo sacrificio in termini di calcolo, ma i risultati si sono dimostrati più robusti.


